/*!
* @file    main.c
* @version v1.0
* @date    2018-02-05
* @author  苏勇 suyong_yq@126.com
* @brief
* 调试ADC模块:
* - ADC使用队列转换
* - 当队列中所有的转换任务结束之后可产生队列完成中断，通知应用程序读数
* - 当启用Burst模式时，队列自动进行连续转换，最近一次的转换结果直接刷新到数据寄存器中
*/

#include "app_inc.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/

/*******************************************************************************
 * Variables
 ******************************************************************************/
volatile bool bAppAdcSeqAConvDone = false;
volatile uint32_t gAppAdcSeqAConvCount = 0U;

/* 在实际上使用时，为节约内存可直接使用ADC内部的数据寄存器，而不必搬运到内存中。
 * 此ADC的硬件允许Overflow，即可以覆盖未取走的数，虽然也会起Overflow的标志位，等效于覆盖内存。
 * 此时，使用Burst模式的效果等价于在每次序列转换完成之后用DMA覆盖内存。
 */
volatile uint32_t gAppAdcSensingValues[12];

char gAppPrintfBuf[64];

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
void App_BootClockRUN(void);
void App_Tick_PeriodicalCallback(void);

void App_AdcSensor_Init(void);
void App_AdcSensor_Start(void);

/*******************************************************************************
 * Code
 ******************************************************************************/
/*!
 * @brief Main function
 */
int main(void)
{
    /* Init board hardware. */
    BOARD_InitBootPins();
    BOARD_BootClockRUN();
    App_BootClockRUN(); /* Enable the clocks additionally not in BOARD_BootClockRUN(). */

    /* Initialize debug console. */
    BOARD_InitDebugConsole(); /* Flexcomm0 - UART0, 115200. */

    PRINTF("\r\nADC_SeqBurst Example.\r\n");
    PRINTF("build time: %s, %s\r\n", __TIME__, __DATE__);

    OLED_Init();
    OLED_PrintReset();
    OLED_PrintStr("HelloWorld\r\n");

    Tick_Init(200UL); /* 5ms per tick. */
    BTN_Init();
    Tick_InstallPeriodicalCallback(1U, App_Tick_PeriodicalCallback);

    App_AdcSensor_Init();
    App_AdcSensor_Start(); /* 启用Burst模式，在ADC的寄存器里自动刷新采样数据 */

    while (1)
    {
#if 0 /* 此处代码用于验证普通软件触发 */
        OLED_PrintStr("Wait for the KEY0\r\n");
        BTN_WaitBlocking(0U); /* 等待KEY0按下并松开 */

        bAppAdcSeqAConvDone = false;
        App_AdcSensor_Start();
        while (!bAppAdcSeqAConvDone)
        {}
				sprintf(gAppPrintfBuf, "Count: %d\r\n", gAppAdcSeqAConvCount);
        OLED_PrintStr(gAppPrintfBuf);
#endif
    }
}

/* 启用在BOARD_BootClockRUN()函数中未启用的时钟 */
void App_BootClockRUN(void)
{
    CLOCK_EnableClock(kCLOCK_Gpio0);
    CLOCK_EnableClock(kCLOCK_Gpio1);
}

void App_Tick_PeriodicalCallback(void)
{
    BTN_WaitBlockingHook();
}

/* 初始化ADC的控制部分
* 使用SeqA作为唯一转换队列（不使用SeqB）
* 转换通道号为：0， 1， 2， 3， 4， 5， 6， 11
*/
void App_AdcSensor_Init(void)
{
    adc_config_t adcConfigStruct;
    adc_conv_seq_config_t adcConvSeqConfigStruct;

    POWER_DisablePD(kPDRUNCFG_PD_ADC0); /* 为ADC转换器的模拟电路上电 */

    /* 配置ADC转换器 */
    adcConfigStruct.clockMode = kADC_ClockSynchronousMode; /* 使用总线时钟作为ADC转换器源 */
    adcConfigStruct.clockDividerNumber = 4U; /* 输入时钟源为来自总线时钟220MHz，此处分频保证输入ADC的时钟源不超过80MHz即可 */
    adcConfigStruct.resolution = kADC_Resolution12bit; /* 最高分辨率12b */
    adcConfigStruct.enableBypassCalibration = false; /* 当开启校准周期时，需要在每次上电之前校准ADC */
    adcConfigStruct.sampleTimeNumber = 5U; /* 稍微延长ADC采样保持的时间 */
    ADC_Init(ADC0, &adcConfigStruct);

    /* 校准ADC的转换系统偏差 */
    while (!ADC_DoSelfCalibration(ADC0))
    {}

    ADC_EnableConvSeqA(ADC0, false); /* 在配置过程中关闭转换队列 */
    /* 设定扫描通道及触发源 */
    adcConvSeqConfigStruct.channelMask = (1U << 0U) | (1U << 1U) | (1U << 2U) | (1U << 3U) | (1U << 4U) | (1U << 5U) | (1U << 6U) | (1U << 11U);
    adcConvSeqConfigStruct.triggerMask = 0U; /* 使用软件触发，禁用一切硬件触发 */
    adcConvSeqConfigStruct.triggerPolarity = kADC_TriggerPolarityPositiveEdge; /* 上升沿触发，此处软件触发，配置无效 */
    adcConvSeqConfigStruct.enableSyncBypass = false; /* 与硬件触发相关，此处软件触发，配置无效 */
    adcConvSeqConfigStruct.enableSingleStep = false; /* 连续转换整个队列，而不是触发一次转换一个通道 */
    adcConvSeqConfigStruct.interruptMode = kADC_InterruptForEachSequence; /* 整个转换结束之后触发中断 */
    ADC_SetConvSeqAConfig(ADC0, &adcConvSeqConfigStruct);
    ADC_EnableConvSeqA(ADC0, true); /* 启用转换队列 */

    /* 起中断 */
    //ADC_EnableInterrupts(ADC0, kADC_ConvSeqAInterruptEnable);
    //NVIC_EnableIRQ(ADC0_SEQA_IRQn);
}

void App_AdcSensor_Start(void)
{
    //ADC_DoSoftwareTriggerConvSeqA(ADC0); /* 普通的软件触发只能触发一次转换队列 */
    ADC_EnableConvSeqABurstMode(ADC0, true); /* Burst模式的触发可以触发转换队列连续转换 */
}

#if 0 /* 用于验证中断模式 */
/* ADC SeqA中断服务程序入口 */
void ADC0_SEQA_IRQHandler(void)
{
    uint32_t flags = ADC_GetStatusFlags(ADC0);

    if (0U != (kADC_ConvSeqAInterruptFlag & flags)) /* 队列转换完成 */
    {
        bAppAdcSeqAConvDone = true;
        gAppAdcSeqAConvCount++;
        gAppAdcSensingValues[0] = ADC0->DAT[0];
        gAppAdcSensingValues[1] = ADC0->DAT[1];
        gAppAdcSensingValues[2] = ADC0->DAT[2];
        gAppAdcSensingValues[3] = ADC0->DAT[3];
        gAppAdcSensingValues[4] = ADC0->DAT[4];
        gAppAdcSensingValues[5] = ADC0->DAT[5];
        gAppAdcSensingValues[6] = ADC0->DAT[6];
        gAppAdcSensingValues[11] = ADC0->DAT[11];
    }

    ADC_ClearStatusFlags(ADC0, flags);
}
#endif

/* EOF. */

